﻿using System;
using System.Data;
using System.Drawing;
using System.IO;
using System.Globalization;
using Subsembly.FinTS;
using Subsembly.Swift;

namespace Home.Haushalt.Lib.Element
{
    /// <summary>
    /// 
    /// </summary>
    [ElementExtension("Element")]
    public class FinAccount : ElementBase
    {
        #region CONSTANTS
        public new static readonly Type ElementType = typeof(FinAccount);

        /// <summary></summary>
        public class Fields
        {
            public const string Id = ELEMENTPROPERTY_ID;
            public const string Label = ELEMENTPROPERTY_LABEL;
            public const string Description = ELEMENTPROPERTY_NOTE;
            public const string Name = "name";
            public const string Bank = "bank";
            public const string BLZ = "blz";
            public const string Kto = "kto";
            public const string RefKto = "refkto";
            public const string Saldo = "saldo";
            public const string LastUpdate = "lastupdate";
            public const string LastChangedCount = "lastchanged";
            public const string ContactFile = "contactfile";
            public const string PIN = "pin";
            public const string Updating = "updating";
            public const string IsBaseKonto = "basekto";
        }
        #endregion CONSTANTS

        /// <summary></summary>
        public override string PropertyFormCaption { get { return (Row != null) ? Row[Fields.Name].ToString() : ""; } }

        /// <summary></summary>
        public event EventHandler OnUpdateData;

        /// <summary></summary>
        private double _loadedClosingBalance = 0;

        /// <summary></summary>
        private FinDate _finDateObj;

        #region PROPERTIES

        /// <summary></summary>
        public DateTime LastUpdate { get { return Row[FinAccount.Fields.LastUpdate].ToDateTime(); } set { Row[FinAccount.Fields.LastUpdate] = value; } }

        /// <summary></summary>
        public double Saldo { get { return Row[FinAccount.Fields.Saldo].ToDouble(); } set { Row[FinAccount.Fields.Saldo] = value; } }

        /// <summary></summary>
        public int LastChangedCount { get { return Row[FinAccount.Fields.LastChangedCount].ToInt(); } set { Row[FinAccount.Fields.LastChangedCount] = value; } }

        /// <summary></summary>
        public string Bank { get { return Row[FinAccount.Fields.Bank].ToString(); } }

        /// <summary></summary>
        public string BLZ { get { return Row[FinAccount.Fields.BLZ].ToString(); } }

        /// <summary></summary>
        public string Name { get { return Row[FinAccount.Fields.Name].ToString(); } }

        /// <summary></summary>
        public string KtoNr { get { return Row[FinAccount.Fields.Kto].ToString(); } }

        /// <summary></summary>
        public string RefKtoNr { get { return Row[FinAccount.Fields.RefKto].ToString(); } }

        /// <summary></summary>
        public string PIN { get { return Row[FinAccount.Fields.PIN].ToString(); } }

        /// <summary></summary>
        public string ContactFile { get { return Path.Combine(DAO.Settings.Selected.DataFullPath, Row[FinAccount.Fields.ContactFile].ToString()); } }

        #endregion PROPERTIES

        /// <summary>
        /// 
        /// </summary>
        public FinAccount() : base() { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parentDataRow"></param>
        public FinAccount(DataRow parentDataRow)
            : base(parentDataRow)
        {

            _finDateObj = new FinDate(this);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override Color GetRowForeColor()
        {
            Color foreColor = base.GetRowForeColor();

            if (CheckColumnsInRow(Fields.Saldo))
            {
                if (Row[Fields.Saldo].ToDouble() >= 0)
                    foreColor = DAO.Settings.Selected.RowStateColorPlus;
                if (Row[Fields.Saldo].ToDouble() < 0)
                    foreColor = DAO.Settings.Selected.RowStateColorMinus;
            }
            return foreColor;
        }

        #region BANKING SUPPORT

        /// <summary>
        /// Lädt über Internet neue Daten von der Bank
        /// </summary>
        /// <returns></returns>
        public DataTable UpdateDates()
        {
            DateTime lastDate = DateTime.MinValue;

            if (_finDateObj.ElementTable.Rows.Count > 0)
            {
                _finDateObj.ElementTable.DefaultView.Sort = FinDate.Fields.EntryDate + " DESC";
                lastDate = _finDateObj.ElementTable.DefaultView[0][FinDate.Fields.EntryDate].ToDateTime();
            }

            DataTable newDateTbl = LoadData(lastDate);


            if (newDateTbl != null && newDateTbl.Rows.Count > 0)
            {
                LastUpdate = DateTime.Now;
                Saldo = _loadedClosingBalance;
                LastChangedCount = 0;

                if (Update(newDateTbl))
                {
                    LastChangedCount = _finDateObj.ElementTable.Select(string.Format("{0} = '1'", FinDate.Fields.New)).Length;

                    _finDateObj.ElementTable.DataSet.WriteXml(_finDateObj.FileName, DAO.Credential.CurrentCry);

                    DAO.SaveFinAccount();

                    if (OnUpdateData != null)
                        OnUpdateData(this, null);
                }
            }


            return _finDateObj.ElementTable;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tblChanges"></param>
        private bool Update(DataTable tblChanges)
        {
            bool baseChanged = false;

            //New-Status zurücksetzen
            foreach (DataRow oldNewRow in _finDateObj.ElementTable.Select(string.Format("{0} = '1'", FinDate.Fields.New)))
            {
                oldNewRow[FinDate.Fields.New] = null;
            }

            //Neue finden
            for (int x = 0; x < tblChanges.Rows.Count; x++)
            {
                DataRow changeRow = tblChanges.Rows[x];

                DataRow[] baseDateRows = _finDateObj.ElementTable.Select(
                    string.Format("{0} = #{1}# and {2} = #{3}#",
                       FinDate.Fields.EntryDate, changeRow[FinDate.Fields.EntryDate].ToDateTime().ToString("d", DateTimeFormatInfo.InvariantInfo),
                       FinDate.Fields.ValueDate, changeRow[FinDate.Fields.ValueDate].ToDateTime().ToString("d", DateTimeFormatInfo.InvariantInfo)
                 ));

                bool found = false;
                foreach (DataRow baseDateRow in baseDateRows)
                {
                    bool rowequals = true;
                    for (int i = 2; i < _finDateObj.ElementTable.Columns.Count; i++)
                    {
                        if (baseDateRow[i].ToString() != changeRow[i].ToString())
                        {
                            rowequals = false;
                            break;
                        }
                    }
                    if (rowequals)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    changeRow[FinDate.Fields.New] = "1";
                    _finDateObj.ElementTable.ImportRow(changeRow);
                    baseChanged = true;
                }
            }

            return baseChanged;
        }

        /// <summary>
        /// Lädt über Internet Daten von der Bank ab einen bestimmten Zeitpunkt
        /// </summary>
        /// <param name="from"></param>
        /// <returns></returns>
        public DataTable LoadData(DateTime from)
        {
            DataTable tbl = null;

            DataSet ds = new DataSet();
            ds.OpenSchema(FinDate.ElementType);
            tbl = ds.Tables[0];

            //FinContact contact = new FinContact("https://hbci.postbank.de/banking/hbci.do", 220, "mobileTAN1", "76010085", ktoNr);
            //if (contact != null)
            //{
            //    contact["DefaultCustID"] = ktoNr;
            //}

            try
            {
                FinContact contact = new FinContact();
                contact.Load(ContactFile);
                FinAcct account = new FinAcct(KtoNr, "280", BLZ);
                account.Currency = "EUR";

                SwiftDate fromDate = new SwiftDate(from);
                SwiftDate toDate = new SwiftDate(DateTime.Now);

                FinAcctMvmtsSpecifiedPeriodBuilder acctMvmtsBuilder = new FinAcctMvmtsSpecifiedPeriodBuilder(contact);

                FinAcctMvmtsSpecifiedPeriod order = acctMvmtsBuilder.Build(account, false, fromDate, toDate, 0, null);

                FinDialog dialog = new FinDialog(contact);
                dialog.InitDialog(FinDialogType.Standard, RefKtoNr, PIN, null);

                dialog.ExecuteOrder(order, null);

                //Dialog beenden 
                dialog.TermDialog();

                SwiftStatementReader aStmtReader = order.BookedTrans;
                if (aStmtReader != null)
                {
                    SwiftStatement aMT940 = SwiftStatement.ReadMT940(aStmtReader, true);

                    _loadedClosingBalance = aMT940.ClosingBalance.DecValue.ToDouble();

                    foreach (SwiftStatementLine aStatementLine in aMT940.StatementLines)
                    {
                        DataRow row = tbl.NewRow();
                        row[FinDate.Fields.EntryDate] = aStatementLine.EntryDate.ToDateTime();
                        row[FinDate.Fields.ValueDate] = aStatementLine.ValueDate.ToDateTime();
                        row[FinDate.Fields.EntryText] = GetEntryText(aStatementLine);
                        row[FinDate.Fields.BankRef] = aStatementLine.BankRef;
                        row[FinDate.Fields.CreditorId] = aStatementLine.CreditorId;
                        row[FinDate.Fields.Currency] = aStatementLine.Currency;
                        row[FinDate.Fields.DecValue] = aStatementLine.DecValue;
                        row[FinDate.Fields.EndToEndId] = aStatementLine.EndToEndId;
                        row[FinDate.Fields.FundsCode] = aStatementLine.FundsCode;
                        row[FinDate.Fields.MandateId] = aStatementLine.MandateId;
                        row[FinDate.Fields.OwnerRef] = aStatementLine.OwnerRef;
                        row[FinDate.Fields.PackedPaymtPurpose] = aStatementLine.PaymtPurpose.ToString("; ");
                        row[FinDate.Fields.PayeePayerAcctNo] = aStatementLine.PayeePayerAcctNo;
                        row[FinDate.Fields.PayeePayerBankCode] = aStatementLine.PayeePayerBankCode;
                        row[FinDate.Fields.PayeePayerName1] = aStatementLine.PayeePayerName1;
                        row[FinDate.Fields.PayeePayerName2] = aStatementLine.PayeePayerName2;
                        row[FinDate.Fields.PrimaNotaNo] = aStatementLine.PrimaNotaNo;
                        row[FinDate.Fields.SupplementaryDetails] = aStatementLine.SupplementaryDetails;
                        row[FinDate.Fields.TextKeyExt] = aStatementLine.TextKeyExt;
                        row[FinDate.Fields.TranTypeIdCode] = aStatementLine.TranTypeIdCode;
                        row[FinDate.Fields.ZkaTranCode] = aStatementLine.ZkaTranCode;
                        tbl.Rows.Add(row);
                    }
                }
                else
                {
                    Journal.Add(this, ActionResult.Nok, "Daten vom Konto [" + Name + "] konnten nicht geladen werden");
                }
            }
            catch (Exception ex)
            {
                Journal.Add(this, "Daten vom Konto [" + Name + "] konnten nicht geladen werden. Hast du die Bank-Connection-Datei vor dem Export in dem Admin-Tool mit PIN synchronisiert?", ex);
            }

            return tbl;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="aStmtLine"></param>
        /// <returns></returns>
        private string GetEntryText(SwiftStatementLine aStmtLine)
        {
            string sEntryText = aStmtLine.EntryText;
            if (sEntryText == null)
            {
                switch (aStmtLine.ZkaTranCode)
                {
                    case "004":
                    case "005":
                        sEntryText = "Lastschrift";
                        break;
                    case "008":
                        sEntryText = "Dauerauftrag";
                        break;
                    case "013":
                        sEntryText = "EU-Standardüberweisung";
                        break;
                    case "020":
                        sEntryText = "Überweisung";
                        break;
                    case "051":
                        sEntryText = "Überweisungsgutschrift";
                        break;
                    case "052":
                        sEntryText = "Dauerauftragsgutschrift";
                        break;
                    default:
                        sEntryText = "Sonstige Buchung";
                        break;
                }
            }

            return sEntryText;
        }



         /*public static void test()
         {
            
             FinContact contact = new FinContact();
             contact.Load("finpb.con");

             FinAcct account = new FinAcct("3250685023", "280", "70110088");
             account.Currency = "EUR";

             SwiftDate fromDate = new SwiftDate(DateTime.MinValue);
             SwiftDate toDate = new SwiftDate(DateTime.Now);

             FinAcctMvmtsSpecifiedPeriodBuilder acctMvmtsBuilder = new FinAcctMvmtsSpecifiedPeriodBuilder(contact);
             try
             {
                 FinAcctMvmtsSpecifiedPeriod order = acctMvmtsBuilder.Build(account, false, fromDate, toDate, 0, null);

                 FinDialog dialog = new FinDialog(contact);
                 dialog.InitDialog(FinDialogType.Standard, "230903857", "66727", null);

                 FinMessage message=dialog.ExecuteOrder(order, null);

                 //Dialog beenden 
                 dialog.TermDialog();

                 SwiftStatementReader aStmtReader = order.BookedTrans;
                 if (aStmtReader != null)
                 {
                     SwiftStatement aMT940 = SwiftStatement.ReadMT940(aStmtReader, true);

                     double _loadedClosingBalance = aMT940.ClosingBalance.DecValue.ToDouble();
                 }
             }
             catch (Exception ex)
             {
             }
         }*/

        #endregion BANKING SUPPORT
    }
}
